gl renderer: Optimize text drawing
authorTimm Bäder <mail@baedert.org>
Thu, 23 Nov 2017 18:49:41 +0000 (19:49 +0100)
committerTimm Bäder <mail@baedert.org>
Thu, 21 Dec 2017 18:12:30 +0000 (19:12 +0100)
Text nodes will almost always end up using the exact same texture and
the same program. So, in that case we can simply add vertex data for all
the characters we need to draw and use just one draw call.

gsk/gl/gskglrenderer.c
gsk/gl/gskglrenderops.c
gsk/gl/gskglrenderopsprivate.h

index 880c108d5656ec7244c8bf11ad442e96ebffb24a..c1532199266319e18475115fdb5109b10e460549 100644 (file)
@@ -328,6 +328,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
       goto out;
     }
   self->coloring_program.index = 3;
+  self->coloring_program.name = "coloring";
   init_common_locations (self, builder, &self->coloring_program);
   INIT_PROGRAM_UNIFORM_LOCATION (coloring_program, color_location, "uColor");
 
@@ -342,6 +343,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
       goto out;
     }
   self->color_matrix_program.index = 4;
+  self->color_matrix_program.name = "color matrix";
   init_common_locations (self, builder, &self->color_matrix_program);
   INIT_PROGRAM_UNIFORM_LOCATION (color_matrix_program, color_matrix_location, "uColorMatrix");
   INIT_PROGRAM_UNIFORM_LOCATION (color_matrix_program, color_offset_location, "uColorOffset");
@@ -357,6 +359,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
       goto out;
     }
   self->linear_gradient_program.index = 5;
+  self->linear_gradient_program.name = "linear gradient";
   init_common_locations (self, builder, &self->linear_gradient_program);
   INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, color_stops_location, "uColorStops");
   INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, color_offsets_location, "uColorOffsets");
@@ -858,6 +861,22 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
         int x = gsk_text_node_get_x (node);
         int y = gsk_text_node_get_y (node);
 
+        /* If the font has color glyphs, we don't need to recolor anything */
+        if (has_color_glyphs)
+          {
+            ops_set_program (builder, &self->blit_program);
+          }
+        else
+          {
+            RenderOp op;
+
+            ops_set_program (builder, &self->coloring_program);
+
+            op.op = OP_CHANGE_COLOR;
+            op.color = *gsk_text_node_peek_color (node);
+            ops_add (builder, &op);
+          }
+
         /* We use one quad per character, unlike the other nodes which
          * use at most one quad altogether */
         for (i = 0; i < num_glyphs; i++)
@@ -888,22 +907,6 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
             cx = (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
             cy = (double)(gi->geometry.y_offset) / PANGO_SCALE;
 
-            /* If the font has color glyphs, we don't need to recolor anything */
-            if (has_color_glyphs)
-              {
-                ops_set_program (builder, &self->blit_program);
-              }
-            else
-              {
-                RenderOp op;
-
-                ops_set_program (builder, &self->coloring_program);
-
-                op.op = OP_CHANGE_COLOR;
-                op.color = *gsk_text_node_peek_color (node);
-                ops_add (builder, &op);
-              }
-
             ops_set_texture (builder, gsk_gl_glyph_cache_get_glyph_image (&self->glyph_cache,
                                                                          glyph)->texture_id);
 
@@ -1156,8 +1159,9 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self,
           break;
 
         case OP_DRAW:
-          OP_PRINT (" -> draw %ld and program %s\n", op->vao_offset, program->name);
-          glDrawArrays (GL_TRIANGLES, op->vao_offset, GL_N_VERTICES);
+          OP_PRINT (" -> draw %ld, size %ld and program %s\n",
+                    op->draw.vao_offset, op->draw.draw_size, program->name);
+          glDrawArrays (GL_TRIANGLES, op->draw.vao_offset, op->draw.draw_size);//GL_N_VERTICES);
           break;
 
         default:
index a1621c6bc07a2855d8cfa063b40bfa9dbe4118d2..6bc9e824aaf50af7f87d8bd4da2c54030d74a12e 100644 (file)
@@ -223,17 +223,45 @@ void
 ops_draw (RenderOpBuilder     *builder,
           const GskQuadVertex  vertex_data[GL_N_VERTICES])
 {
-  RenderOp op;
-  gsize offset = builder->buffer_size / sizeof (GskQuadVertex);
+  RenderOp *last_op;
 
-  op.op = OP_CHANGE_VAO;
-  memcpy (&op.vertex_data, vertex_data, sizeof(GskQuadVertex) * GL_N_VERTICES);
-  g_array_append_val (builder->render_ops, op);
-  builder->buffer_size += sizeof (GskQuadVertex) * GL_N_VERTICES;
+  last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
+  /* If the previous op was a DRAW as well, we didn't change anything between the two calls,
+   * so these are just 2 subsequent draw calls. Same VAO, same program etc.
+   * And the offsets into the vao are in order as well, so make it one draw call. */
+  if (last_op->op == OP_DRAW)
+    {
+      /* We allow ourselves a little trick here. We still have to add a CHANGE_VAO op for
+       * this draw call so we can add our vertex data there, but we want it to be placed before
+       * the last draw call, so we reorder those. */
+      RenderOp new_draw;
+      new_draw.op = OP_DRAW;
+      new_draw.draw.vao_offset = last_op->draw.vao_offset;
+      new_draw.draw.draw_size = last_op->draw.draw_size + GL_N_VERTICES;
+
+      last_op->op = OP_CHANGE_VAO;
+      memcpy (&last_op->vertex_data, vertex_data, sizeof(GskQuadVertex) * GL_N_VERTICES);
+
+      /* Now add the DRAW */
+      g_array_append_val (builder->render_ops, new_draw);
+    }
+  else
+    {
+      RenderOp op;
+      gsize offset = builder->buffer_size / sizeof (GskQuadVertex);
 
-  op.op = OP_DRAW;
-  op.vao_offset = offset;
-  g_array_append_val (builder->render_ops, op);
+      op.op = OP_CHANGE_VAO;
+      memcpy (&op.vertex_data, vertex_data, sizeof(GskQuadVertex) * GL_N_VERTICES);
+      g_array_append_val (builder->render_ops, op);
+
+      op.op = OP_DRAW;
+      op.draw.vao_offset = offset;
+      op.draw.draw_size = GL_N_VERTICES;
+      g_array_append_val (builder->render_ops, op);
+    }
+
+  /* We added new vertex data in both cases so increase the buffer size */
+  builder->buffer_size += sizeof (GskQuadVertex) * GL_N_VERTICES;
 }
 
 void
index 11d69df6d136a0f515fbb56bac513d8e52f873fe..fee25af75cf4430a30d2c262d62fc0e9ddb109c6 100644 (file)
@@ -85,7 +85,6 @@ typedef struct
     int texture_id;
     int render_target_id;
     GdkRGBA color;
-    gsize vao_offset;
     GskQuadVertex vertex_data[6];
     GskRoundedRect clip;
     graphene_rect_t viewport;
@@ -96,6 +95,10 @@ typedef struct
       graphene_point_t start_point;
       graphene_point_t end_point;
     } linear_gradient;
+    struct {
+      gsize vao_offset;
+      gsize draw_size;
+    } draw;
   };
 } RenderOp;